/**
 * ecmacompat.js
 * Copyright(c) Zhang Jian <dsonet@msn.com>
 * This library is free.  You can redistribute it and/or modify it.
 *
 * vim:set autoindent shiftwidth=4 tabstop=4 noexpandtab: LF, UTF-8, no-BOM
 * @creator DSONet<dsonet@msn.com>
 * @version $Id: ecmacompat.js 104 2010-01-07 12:03:16Z dsonet@gmail.com $
 */

(function(context)
{
function extend(cls, prop, self)
{
	var p = self ? cls : cls.prototype, o;

	for (o in prop)
	{
		if (!p[o])
		{
			p[o] = prop[o];
		}
	}
}
//http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/
extend(Array,
{
	/**
	* Checks whether or not the specified object exists in the array.
	* Returns the first (least) index of an element within the array equal to the specified value, or -1 if none is found.
	* @param {Object} o The object to check for
	* @return {Number} The index of o in the array (or -1 if it is not found)
	*/
	indexOf: function(o, i)
	{
		var l = this.length;
		i = i ? i < 0 ? Math.max(0, l + i) : i : 0;

		while (i < l)
		{
			if (i in this && this[i] === o)
				return i;
			++i;
		}
		return -1;
	},
	/**
	* https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Array/lastIndexOf
	* Returns the last (greatest) index of an element within the array equal to the specified value, or -1 if none is found.
	* @param {Object} o The object to check for
	* @return {Number} The index of o in the array (or -1 if it is not found)
	*/
	lastIndexOf: function(o, i)
	{
		var l = this.length;
		i = i ? i < 0 ? Math.max(0, l + i) : ++i : l;

		while (i)
		{
			if (--i in this && this[i] === o)
				return i;
		}
		return -1;
	},
	/**
	* Creates a new array with all of the elements of this array for which the provided filtering function returns true.
	* @param {Function} f Function to test each element of the array.
	* @param {Object} o Object to use as this when executing callback.
	* @return {Array} 
	*/
	filter: function(f, o)
	{
		var l = this.length - 1, i = -1, ret = [], v;

		while (i < l)
		{
			if (++i in this && f.call(o, v = this[i], i, this))
				ret.push(v);
		}
		return ret;
	},
	/**
	* Calls a function for each element in the array.
	* forEach executes the provided function (callback) once for each element present in the array. callback is invoked only for indexes of the array which have assigned values; it is not invoked for indexes which have been deleted or which have never been assigned values.
	* @param {Function} f Function to execute for each element.
	* @param {Object} [o] Object to use as this when executing callback.
	* @return {Boolean} 
	*/
	forEach: function(f, o)
	{
		var l = this.length - 1, i = -1;

		while (i < l)
		{
			if (++i in this)
				f.call(o, this[i], i, this);
		}
	},
	/**
	 * Returns true if every element in this array satisfies the provided testing function.
	 * @param {Function} f Function to test for each element.
	 * @param {Object} o Object to use as this when executing callback.
	 * @return {Boolean} 
	 */
	every: function(f, o)
	{
		var l = this.length - 1, i = -1;

		while (i < l)
		{
			if (++i in this && !f.call(o, this[i], i, this))
			{
				return false;
			}
		}
		return true;
	},
	/**
	 * Creates a new array with the results of calling a provided function on every element in this array.
	 * @param {Function} f Function to test for each element.
	 * @param {Object} o Object to use as this when executing callback.
	 * @return {Array} 
	 */
	map: function(f, o)
	{
		var l = this.length - 1, i = -1, ret = [];

		while (i < l)
		{
			if (++i in this)
				ret.push(f.call(o, this[i], i, this));
		}
		return ret;
	},
	/**
	 * Tests whether some element in the array passes the test implemented by the provided function.
	 * @param {Function} f Function to test for each element.
	 * @param {Object} o Object to use as this when executing callback.
	 * @return {Boolean}
	 */
	some: function(f, o)
	{
		var l = this.length - 1, i = -1;

		while (i < l)
		{
			if (++i in this && f.call(o, this[i], i, this))
			{
				return true;
			}
		}
		return false;
	},
	/**
	* Apply a function simultaneously against two values of the array (from left-to-right) as to reduce it to a single value.
	* @param {Function} f Function to execute on each value in the array.
	* @param {Object} o Object to use as the first argument to the first call of the callback.
	* @return {Number} 
	*/
	reduce: function(f, initial)
	{
		var l = this.length - 1, i = -1, al = arguments.length, rv;

		// no value to return if no initial value and an empty array
		if (l < 0 && al === 1)
			throw 'TypeError';

		if (al >= 2)
		{
			rv = initial;
		}
		else
		{
			while (i < l)
			{
				if (++i in this)
				{
					rv = this[i];
					break;
				}
				// if array contains no values, no initial value to return
				else if (i >= l)
					throw 'TypeError';
			}
		}

		while (i < l)
		{
			if (++i in this)
				rv = f(rv, this[i], i, this);
		}
		return rv;
	},
	/**
	* Apply a function simultaneously against two values of the array (from right-to-left) as to reduce it to a single value.
	* @param {Function} f Function to execute on each value in the array.
	* @param {Object} o Object to use as the first argument to the first call of the callback.
	* @return {Number} 
	*/
	reduceRight: function(f, initial)
	{
		var i = this.length, al = arguments.length, rv;

		// no value to return if no initial value and an empty array
		if (i < 1 && al === 1)
			throw 'TypeError';

		if (al >= 2)
		{
			rv = initial;
		}
		else
		{
			while (i)
			{
				if (--i in this)
				{
					rv = this[i];
					break;
				}
				// if array contains no values, no initial value to return
				else if (i <= 0)
					throw 'TypeError';
			}
		}

		while (i)
		{
			if (--i in this)
				rv = f(rv, this[i], i, this);
		}
		return rv;
	}
});
var RE_WHITESPACE_START = /^\s\s*/;//for speed issue, this style maybe faster?
var RE_NON_WHITESPACE = /\S/;
extend(String, 
{
	trim : function(){
		var str = this.replace(RE_WHITESPACE_START, "");
		var i = str.length;
		while(i > 0)
		{
			if(RE_NON_WHITESPACE.test(str.charAt(--i)))
			{
				return str.substring(0, i + 1);
			}
		}
		return str;
	}
});
extend(Function, 
{
	/**
	 * @param {Object} context the 'this' value to be used.
	 * @param {arguments} [1..n] optional arguments that are
	 * prepended to returned function's call.
	 * @return {Function} a function that applies the original
	 * function with 'context' as the thisArg.
	 */
	bind: function(context)
	{
		var fn = this, args;

		// Strategy 1: just bind, not a partialApply
		if (arguments.length === 1)//we don't consider none arguments here
		{
			return function()
			{
				return arguments.length !== 0 ? fn.apply(context, arguments) : fn.call(context);// faster in Firefox.
			};
		}
		else
		{
			// Strategy 2: partialApply
			args = Array.prototype.slice.call(arguments, 1);
			return function()
			{
				return fn.apply(context, arguments.length === 0 ? args : Array.prototype.concat.apply(args, arguments));
			};
		}
	}
});
extend(Object, 
{
	keys: function( obj ) 
	{
		var array = [];
		for ( var prop in obj ) 
		{
			if ( obj.hasOwnProperty( prop ) ) 
			{
				array.push( prop );
			}
		}
		return array;
	},
	getPrototypeOf: typeof "".__proto__ === "object" ? function(object)
	{
		return object.__proto__;
    } : function(object)
	{
		// May break if the constructor has been tampered with
		return object.constructor.prototype;
    }
}, true);
extend(Date, 
{
	now: function() 
	{
		return (new Date).getTime();
	}
}, true);
extend(Date, 
{
	toISOString: function()
	{
		function padStr(i)
		{
			return i < 10 ? "0" + i : i;
		}
		var ms = this.getUTCMilliseconds();

		if (ms < 10)
		{
			ms = "00" + ms;
		}
		else if (ms < 100)
		{
			ms = "0" + ms;
		}
		else if (ms > 999)
		{
			ms = Math.round(ms / Math.pow(10, ("" + ms).length - 3));
		}
		return this.getUTCFullYear() + "-"
			+ padStr(this.getUTCMonth() + 1) + "-"
			+ padStr(this.getUTCDate()) + "T"
			+ padStr(this.getUTCHours()) + ":"
			+ padStr(this.getUTCMinutes()) + ":"
			+ padStr(this.getUTCSeconds()) + "."
			+ ms + "Z";
	},
	toJSON: function()
	{
		return (typeof this === "number" && !isFinite(this) ? null : this.toISOString());
	}
});
//some part from http://erik.eae.net/playground/js16generics.js
// Make generic versions of instance methods
[
	{
		cls: Array,
		methods:
		[
			"join",
			"reverse",
			"sort",
			"push",
			"pop",
			"shift",
			"unshift",
			"splice",
			"concat",
			"slice",
			"indexOf",
			"lastIndexOf",
			"forEach",
			"map",
			"filter",
			"some",
			"every",
			"reduce",
			"reduceRight"
		]
	},
	{
		cls: String,
		methods:
		[
			"trim",
			"substring",
			"toLowerCase",
			"toUpperCase",
			"charAt",
			"charCodeAt",
			"indexOf",
			"lastIndexOf",
			"toLocaleLowerCase",
			"toLocaleUpperCase",
			"localeCompare",
			"match",
			"search",
			"replace",
			"split",
			"substr",
			"concat",
			"slice"
		]
	}
].forEach(function(it) 
{
	var cls = it.cls;
	it.methods.forEach(function(name)
	{
		if (!cls[name])
		{
			cls[name] = function (s)
			{
				var args = Array.prototype.slice.call(arguments, 1);
				return cls.prototype[name].apply(s, args);
			};
		}
	});
});
})(this);